package com.wosai.register.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;

public class HtmlUtil {
    private static Log logger = LogFactory.getLog(HtmlUtil.class);

    
    
    /**
     * substantiate src attributes of image elements given imageid.
     * @param pureContent
     * @return
     */
    public static void expandImages(Document doc, String baseUrl)
    {
        try {

            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath xpath = xpathFactory.newXPath();

            NodeList nodes = (NodeList)xpath.evaluate("//img[@type='IMAGE' and @imageid]", doc.getDocumentElement(), XPathConstants.NODESET);

            for(int i=0; i<nodes.getLength(); ++i){
                Node node = nodes.item(i);
                if (node.getNodeType() != Node.ELEMENT_NODE)
                    continue;
                
                Element elm = (Element)node;
                
                String imageid = elm.getAttribute("imageid");
                
                String imageUrl = baseUrl + "/" + imageid;
                
                elm.setAttribute("src", imageUrl);
            }
            
        }
        catch (Exception e) {
            logger.warn(ExceptionUtils.getStackTrace(e));
        }
    }

    /**
     * remove src attributes from image elements.
     * @param editedContent
     * @return
     */
    public static void reduceImages(Document doc)
    {
        try {

            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath xpath = xpathFactory.newXPath();

            NodeList nodes = (NodeList)xpath.evaluate("//img[@type='IMAGE' and @src]", doc.getDocumentElement(), XPathConstants.NODESET);

            for(int i=0; i<nodes.getLength(); ++i){
                Node node = nodes.item(i);
                if (node.getNodeType() != Node.ELEMENT_NODE)
                    continue;
                
                Element elm = (Element)node;
                
                elm.removeAttribute("src");
                
            }
            
        }
        catch (Exception e) {
            logger.warn(ExceptionUtils.getStackTrace(e));
        }
    }
    
    public static void addPlaceholderImages(Document doc, int placeholderImageId)
    {
        try {

            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath xpath = xpathFactory.newXPath();

            NodeList nodes = (NodeList)xpath.evaluate("//img[@type='IMAGE' and not(@imageid) ]", doc.getDocumentElement(), XPathConstants.NODESET);

            for(int i=0; i<nodes.getLength(); ++i) {
                Node node = nodes.item(i);
                if (node.getNodeType() != Node.ELEMENT_NODE)
                    continue;
                
                Element elm = (Element)node;
                
                elm.setAttribute("imageid", String.valueOf(placeholderImageId));
            }
            
        }
        catch (Exception e) {
            logger.warn(ExceptionUtils.getStackTrace(e));
        }
        
    }
    
    public static Document applyTemplate(Document doc, Document template, int placeholderImageId)
    {
        Document target = template;
        Map<String, String> sections = getSections(doc);
        
        try {

            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath xpath = xpathFactory.newXPath();

            NodeList nodes = (NodeList)xpath.evaluate("//img[@type='IMAGE'] | //p[@type='TEXT'] ", target.getDocumentElement(), XPathConstants.NODESET);

            for(int i=0; i<nodes.getLength(); ++i) {
                Node node = nodes.item(i);
                if (node.getNodeType() != Node.ELEMENT_NODE)
                    continue;
                
                Element elm = (Element)node;
                
                String id = elm.getAttribute("id");
                String type = elm.getAttribute("type");
                
                                
                if ("IMAGE".equals(type)){
                    if (sections.get(id) == null){
                        elm.setAttribute("imageid", String.valueOf(placeholderImageId));
                    }else{
                        elm.setAttribute("imageid", sections.get(id));
                    }
                }else if ("TEXT".equals(type)){
                    if (sections.get(id)!=null){
                        elm.setTextContent(sections.get(id));
                    }
                }else{
                    logger.warn("Unknown element type " + elm);
                }
            }
            return target;
        }
        catch (Exception e) {
            logger.warn(ExceptionUtils.getStackTrace(e));
        }
        return null;
    }
    
    public static String tidy(String input)
    {
        StringReader reader = new StringReader(input);

        Tidy tidy = new Tidy();
        tidy.setXHTML(true);
        tidy.setQuiet(true);
        tidy.setShowWarnings(false);
        tidy.setPrintBodyOnly(true);

        StringWriter writer = new StringWriter();

        tidy.parse(reader, writer);

        return writer.toString();
    }

    public static Document parse(InputStream is)
    {
        
        try {
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return builder.parse(is);
        }
        catch (Exception e) {
            logger.warn(ExceptionUtils.getStackTrace(e));
            return null;
        }
    }

    public static void write(Document document, OutputStream os)
    {
        
        try {
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            DOMSource source = new DOMSource(document);
            StreamResult result = new StreamResult(os);
            transformer.transform(source, result);
        }
        catch (Exception e) {
            logger.warn(ExceptionUtils.getStackTrace(e));
        }
    }
    
    public static Document string2document(String str)
    {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes("UTF-8"));
            Document doc = parse(bais);
            return doc;
        }catch(UnsupportedEncodingException e){
            logger.warn(ExceptionUtils.getStackTrace(e));
        }
        return null;
    }
    
    public static String document2string(Document doc)
    {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            write(doc, baos);
            return new String(baos.toByteArray(), "UTF-8");
        }catch(UnsupportedEncodingException e){
            logger.warn(ExceptionUtils.getStackTrace(e));
        }
        return null;
    }

    public static Map<String, String> getSections(Document document)
    {
        try{

            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath xpath = xpathFactory.newXPath();

            Map<String, String> sections = new HashMap<String, String>();
            
            NodeList nodes = (NodeList)xpath.evaluate("//img[@type='IMAGE' and @imageid] | //p[@type='TEXT'] ", document.getDocumentElement(), XPathConstants.NODESET);
            for(int i=0; i<nodes.getLength(); ++i){
                Node node = nodes.item(i);
                if (node.getNodeType() != Node.ELEMENT_NODE)
                    continue;
                
                Element elm = (Element)node;
                
                String id = elm.getAttribute("id");
                String type = elm.getAttribute("type");
                if ("IMAGE".equals(type)){
                    String imageid = elm.getAttribute("imageid");
                    sections.put(id, imageid);
                    
                }else if ("TEXT".equals(type)){
                    String text = elm.getTextContent();
                    sections.put(id, text);
                    
                }else{
                    logger.warn("unknown element type " + elm);
                }
            }
            
            return sections;

        }catch(Exception e){
            logger.warn(ExceptionUtils.getStackTrace(e));
            return Collections.emptyMap();
        }
    }

    
    public static void main(String[] args)
    {
        String template = "<div id=\"container\"> \n" +
                          "  <img id=\"image_1\" type=\"IMAGE\"></img> \n" +
                          "  <p id=\"text_1\" type=\"TEXT\">Enter your text here ...</p> \n" +
                          "  <img id=\"image_2\" type=\"IMAGE\"></img> \n" +
                          "  <p id=\"text_2\" type=\"TEXT\">Enter your text here ...</p> \n" +
                          "  <img id=\"image_3\" type=\"IMAGE\"></img> \n" +
                          "</div>";
        
        String edited = "<div id=\"container\"> \n" +
                          "  <img id=\"image_1\" type=\"IMAGE\" imageid=\"2\"> \n" +
                          "  <img id=\"image_2\" type=\"IMAGE\" imageid=\"3\"> \n" +
                          "  <p id=\"text_1\" type=\"TEXT\">modified text 1</p> \n" +
                          "  <p id=\"text_2\" type=\"TEXT\">modified text 2</p> \n" +
                          "  <p id=\"text_3\" type=\"TEXT\">modified text 3</p> \n" +
                          "</div>";

        
        Document doc = string2document(template);
        System.err.println("template: " + document2string(doc));
        
        String fixed = tidy(edited);
        System.err.println("fixed: " + fixed);
        
        Document doc2 = string2document(fixed);
        System.err.println("edited: " + document2string(doc2));
        
        Document doc3 = applyTemplate(doc2, doc, 1);
        /*
        addPlaceholderImages(doc, 1);
        
        expandImages(doc, "/media");
        
        
        System.err.println("placeholder images expanded: " + document2string(doc));
        
        reduceImages(doc);
        System.err.println("images reduced: " + document2string(doc));
        */
        System.err.println("applied template: " + document2string(doc3));
    }
}
